package IC.Types;

import java.util.*;
import IC.AST.ICClass;
import IC.SemanticCheckes.SemanticError;


public class TypeTable {

    public static Map<String, Type> getUniquePrimitiveTypes() {
		return uniquePrimitiveTypes;
	}

    public static void initTypeTable(String icFileName){
    	// save file name
    	TypeTable.icFileName = icFileName;
    	
    	uniquePrimitiveTypes.put("string", new PrimitivesType("string"));
    	uniquePrimitiveTypes.put("int", new PrimitivesType("int"));
    	uniquePrimitiveTypes.put("boolean", new PrimitivesType("boolean"));
    	uniquePrimitiveTypes.put("void", new PrimitivesType("void"));
    	uniquePrimitiveTypes.put("null", new PrimitivesType("null"));
    }
    
    public static MethodType methodType(Type returnType, List<Type> paramTypes){
    	MethodType mt = new MethodType(returnType,paramTypes);
    	String key = mt.toString();	
    	MethodType mt2 = uniqueMethodTypes.get(key);
    	if (mt2 == null) {
    		uniqueMethodTypes.put(key, mt);
    		return mt;
    	} else {
    		return mt2;
    	}
    	
    }
    
    public static ArrayType arrayType(Type elemType) {
       if (uniqueArrayTypes.containsKey(elemType)) {
          return uniqueArrayTypes.get(elemType);
       }
       else {
          ArrayType arrt = new ArrayType(elemType);
          uniqueArrayTypes.put(elemType,arrt);
          return arrt;
       }
    }
    
    public static void addClassType(ICClass c) throws SemanticError{
    	if (uniqueClassTypes.containsKey(c.getName())){ 
    		throw new SemanticError("class already defined",c.getLine(),c.getName());
    	}
    	if (c.hasSuperClass()) {
    		if (!uniqueClassTypes.containsKey(c.getSuperClassName())){
    			throw new SemanticError("super-class is undefined",c.getLine(),c.getSuperClassName());
    		}
    	}
    	
    	ClassType ct = new ClassType(c);
    	uniqueClassTypes.put(c.getName(),ct);
    }
    
    public static ClassType getClassType(String name) throws SemanticError{
    	ClassType ct = uniqueClassTypes.get(name);
    	if (ct == null) {
    		throw new SemanticError("class is undefined",name);
    	}
    	else{
    		return ct;
    	}
    }
    
    public static Type getType(String typeName) throws SemanticError{
    	Type t;  	
    	// is primitive type
    	t = uniquePrimitiveTypes.get(typeName);
    	if (t != null) {
    		return t;
    	}
    	// is array type
    	if (typeName.endsWith("[]")) {
    		return arrayType(getType(typeName.substring(0, typeName.length()-2)));
    	}
    	// is class type
    	else {
    		return getClassType(typeName);
    	}
    }
    
    public static String getFileName(){
    	return icFileName;
    }
    
    public static String PrintSymTable(){
    	String str = "Type Table: "+icFileName+"\n";
    	
    	// primitive types
    	Iterator<Type> uniquePrimitiveTypesIter = uniquePrimitiveTypes.values().iterator();
    	String primitiveTypesStr = "";
    	while (uniquePrimitiveTypesIter.hasNext()){
    		Type t = uniquePrimitiveTypesIter.next();
    		primitiveTypesStr += "\t"+t.getTypeID()+": Primitive type: "+t.getName()+"\n";
    	}
    	
    	// class types
    	Iterator<ClassType> uniqueClassTypesIter = uniqueClassTypes.values().iterator();
    	String classTypesStr = "";
    	while (uniqueClassTypesIter.hasNext()){
    		ClassType ct = uniqueClassTypesIter.next();
    		classTypesStr += "\t"+ct.getTypeID()+": Class: "+ct.toString()+"\n";
    	}
    	
    	// array types
    	Iterator<ArrayType> uniqueArrayTypesIter = uniqueArrayTypes.values().iterator();
    	String arrayTypesStr = "";
    	while (uniqueArrayTypesIter.hasNext()){
    		ArrayType at = uniqueArrayTypesIter.next();
    		arrayTypesStr += "\t"+at.getTypeID()+": Array type: "+at.toString()+"\n";
    	}
    	
    	// method types
    	String methodTypesStr = "";
    	for (MethodType mt: uniqueMethodTypes.values()){
    		methodTypesStr += "\t"+mt.getTypeID()+": Method type: "+mt.toString()+"\n";
    	}
    	
    	str += primitiveTypesStr+classTypesStr+arrayTypesStr+methodTypesStr;
    	return str;
    }
    
	protected static int idCounter = 0;
    private static String icFileName = null;
    private static Map<String,Type> uniquePrimitiveTypes = new HashMap<String,Type>();   
    private static Map<Type,ArrayType> uniqueArrayTypes = new HashMap<Type,ArrayType>();   
    private static Map<String,ClassType> uniqueClassTypes = new HashMap<String,ClassType>();    
    private static Map<String,MethodType> uniqueMethodTypes = new HashMap<String,MethodType>();    
    
}
